import { ref, readonly, onMounted, onUnmounted, type Ref } from 'vue'

// Extend CSSStyleDeclaration to include webkit properties
declare global {
  interface CSSStyleDeclaration {
    webkitTouchCallout?: string
    webkitTapHighlightColor?: string
    webkitOverflowScrolling?: string
    webkitUserSelect?: string
  }
}

export interface TouchPoint {
  x: number
  y: number
  timestamp: number
}

export interface SwipeDirection {
  direction: 'left' | 'right' | 'up' | 'down'
  distance: number
  velocity: number
  duration: number
}

export interface TouchGestureOptions {
  swipeThreshold?: number
  velocityThreshold?: number
  preventScroll?: boolean
  enablePinch?: boolean
}

/**
 * Touch gesture composable for handling swipe, tap, and pinch gestures
 */
export function useTouchGestures(
  element: Ref<HTMLElement | undefined>,
  options: TouchGestureOptions = {}
) {
  const {
    swipeThreshold = 50,
    velocityThreshold = 0.3,
    preventScroll = false,
    enablePinch = false
  } = options

  // Touch state
  const isTouch = ref(false)
  const touchStart = ref<TouchPoint | null>(null)
  const touchEnd = ref<TouchPoint | null>(null)
  const touchDistance = ref(0)
  const touchVelocity = ref(0)
  const lastTouchTime = ref(0)

  // Pinch state
  const isPinching = ref(false)
  const pinchScale = ref(1)
  const initialPinchDistance = ref(0)

  // Event handlers
  const handleTouchStart = (event: TouchEvent) => {
    const touch = event.touches[0]
    const now = Date.now()
    
    touchStart.value = {
      x: touch.clientX,
      y: touch.clientY,
      timestamp: now
    }
    
    isTouch.value = true
    lastTouchTime.value = now

    // Handle pinch gesture
    if (enablePinch && event.touches.length === 2) {
      const touch1 = event.touches[0]
      const touch2 = event.touches[1]
      const distance = Math.sqrt(
        Math.pow(touch2.clientX - touch1.clientX, 2) +
        Math.pow(touch2.clientY - touch1.clientY, 2)
      )
      
      isPinching.value = true
      initialPinchDistance.value = distance
      pinchScale.value = 1
    }

    if (preventScroll) {
      event.preventDefault()
    }
  }

  const handleTouchMove = (event: TouchEvent) => {
    if (!touchStart.value) return

    const touch = event.touches[0]
    const now = Date.now()
    
    touchEnd.value = {
      x: touch.clientX,
      y: touch.clientY,
      timestamp: now
    }

    // Calculate distance and velocity
    const deltaX = touchEnd.value.x - touchStart.value.x
    const deltaY = touchEnd.value.y - touchStart.value.y
    touchDistance.value = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
    
    const deltaTime = now - touchStart.value.timestamp
    touchVelocity.value = deltaTime > 0 ? touchDistance.value / deltaTime : 0

    // Handle pinch gesture
    if (enablePinch && isPinching.value && event.touches.length === 2) {
      const touch1 = event.touches[0]
      const touch2 = event.touches[1]
      const distance = Math.sqrt(
        Math.pow(touch2.clientX - touch1.clientX, 2) +
        Math.pow(touch2.clientY - touch1.clientY, 2)
      )
      
      if (initialPinchDistance.value > 0) {
        pinchScale.value = distance / initialPinchDistance.value
        emitPinch(pinchScale.value)
      }
    }

    if (preventScroll) {
      event.preventDefault()
    }
  }

  const handleTouchEnd = (event: TouchEvent) => {
    if (!touchStart.value || !touchEnd.value) {
      isTouch.value = false
      isPinching.value = false
      return
    }

    const deltaX = touchEnd.value.x - touchStart.value.x
    const deltaY = touchEnd.value.y - touchStart.value.y
    const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
    const duration = touchEnd.value.timestamp - touchStart.value.timestamp
    const velocity = duration > 0 ? distance / duration : 0

    // Detect swipe gesture
    if (distance >= swipeThreshold && velocity >= velocityThreshold) {
      const absX = Math.abs(deltaX)
      const absY = Math.abs(deltaY)
      
      let direction: SwipeDirection['direction']
      if (absX > absY) {
        direction = deltaX > 0 ? 'right' : 'left'
      } else {
        direction = deltaY > 0 ? 'down' : 'up'
      }

      const swipeData: SwipeDirection = {
        direction,
        distance,
        velocity,
        duration
      }

      // Emit swipe event
      emitSwipe(swipeData)
    } else if (distance < 10 && duration < 300) {
      // Detect tap gesture
      emitTap({
        x: touchEnd.value.x,
        y: touchEnd.value.y,
        duration
      })
    }

    // Reset state
    isTouch.value = false
    isPinching.value = false
    touchStart.value = null
    touchEnd.value = null
    touchDistance.value = 0
    touchVelocity.value = 0
    pinchScale.value = 1
    initialPinchDistance.value = 0

    if (preventScroll) {
      event.preventDefault()
    }
  }

  // Event emitters
  const swipeCallbacks = ref<((swipe: SwipeDirection) => void)[]>([])
  const tapCallbacks = ref<((tap: { x: number; y: number; duration: number }) => void)[]>([])
  const pinchCallbacks = ref<((scale: number) => void)[]>([])

  const emitSwipe = (swipe: SwipeDirection) => {
    swipeCallbacks.value.forEach(callback => callback(swipe))
  }

  const emitTap = (tap: { x: number; y: number; duration: number }) => {
    tapCallbacks.value.forEach(callback => callback(tap))
  }

  const emitPinch = (scale: number) => {
    pinchCallbacks.value.forEach(callback => callback(scale))
  }

  // Public API
  const onSwipe = (callback: (swipe: SwipeDirection) => void) => {
    swipeCallbacks.value.push(callback)
    return () => {
      const index = swipeCallbacks.value.indexOf(callback)
      if (index > -1) {
        swipeCallbacks.value.splice(index, 1)
      }
    }
  }

  const onTap = (callback: (tap: { x: number; y: number; duration: number }) => void) => {
    tapCallbacks.value.push(callback)
    return () => {
      const index = tapCallbacks.value.indexOf(callback)
      if (index > -1) {
        tapCallbacks.value.splice(index, 1)
      }
    }
  }

  const onPinch = (callback: (scale: number) => void) => {
    pinchCallbacks.value.push(callback)
    return () => {
      const index = pinchCallbacks.value.indexOf(callback)
      if (index > -1) {
        pinchCallbacks.value.splice(index, 1)
      }
    }
  }

  // Lifecycle
  onMounted(() => {
    const el = element.value
    if (!el) return

    el.addEventListener('touchstart', handleTouchStart, { passive: !preventScroll })
    el.addEventListener('touchmove', handleTouchMove, { passive: !preventScroll })
    el.addEventListener('touchend', handleTouchEnd, { passive: !preventScroll })
  })

  onUnmounted(() => {
    const el = element.value
    if (!el) return

    el.removeEventListener('touchstart', handleTouchStart)
    el.removeEventListener('touchmove', handleTouchMove)
    el.removeEventListener('touchend', handleTouchEnd)
  })

  return {
    // State
    isTouch: readonly(isTouch),
    touchDistance: readonly(touchDistance),
    touchVelocity: readonly(touchVelocity),
    isPinching: readonly(isPinching),
    pinchScale: readonly(pinchScale),
    
    // Event handlers
    onSwipe,
    onTap,
    onPinch
  }
}

// Helper function for detecting touch device
export function isTouchDevice(): boolean {
  return 'ontouchstart' in window || navigator.maxTouchPoints > 0
}

// Helper function for optimizing touch targets
export function optimizeTouchTarget(element: HTMLElement, minSize = 44) {
  const rect = element.getBoundingClientRect()
  const currentSize = Math.min(rect.width, rect.height)
  
  if (currentSize < minSize) {
    const padding = (minSize - currentSize) / 2
    element.style.padding = `${padding}px`
  }
}

// Enhanced touch interaction utilities
export function enhanceTouchInteraction(element: HTMLElement, options: {
  minTouchSize?: number
  hapticFeedback?: boolean
  preventDoubleClick?: boolean
  optimizeScrolling?: boolean
} = {}) {
  const {
    minTouchSize = 44,
    hapticFeedback = false,
    preventDoubleClick = true,
    optimizeScrolling = true
  } = options

  // Optimize touch target size
  optimizeTouchTarget(element, minTouchSize)

  // Add touch-friendly styles
  element.style.cursor = 'pointer'
  element.style.userSelect = 'none'
  if (element.style.webkitUserSelect !== undefined) {
    element.style.webkitUserSelect = 'none'
  }
  if (element.style.webkitTouchCallout !== undefined) {
    element.style.webkitTouchCallout = 'none'
  }
  if (element.style.webkitTapHighlightColor !== undefined) {
    element.style.webkitTapHighlightColor = 'transparent'
  }

  // Prevent double-click zoom on iOS
  if (preventDoubleClick) {
    element.style.touchAction = 'manipulation'
  }

  // Optimize scrolling performance
  if (optimizeScrolling) {
    element.style.webkitOverflowScrolling = 'touch'
    element.style.overscrollBehavior = 'contain'
  }

  // Add haptic feedback (if supported)
  if (hapticFeedback && 'vibrate' in navigator) {
    const handleTouchStart = () => {
      navigator.vibrate(10) // Light haptic feedback
    }
    
    element.addEventListener('touchstart', handleTouchStart, { passive: true })
    
    return () => {
      element.removeEventListener('touchstart', handleTouchStart)
    }
  }

  return () => {
    // Cleanup function
  }
}

// Touch area expansion for small elements
export function expandTouchArea(element: HTMLElement, expansion = 8) {
  const wrapper = document.createElement('div')
  wrapper.style.position = 'relative'
  wrapper.style.display = 'inline-block'
  
  const touchArea = document.createElement('div')
  touchArea.style.position = 'absolute'
  touchArea.style.top = `-${expansion}px`
  touchArea.style.left = `-${expansion}px`
  touchArea.style.right = `-${expansion}px`
  touchArea.style.bottom = `-${expansion}px`
  touchArea.style.zIndex = '1'
  touchArea.style.cursor = 'pointer'
  
  // Insert wrapper before element
  element.parentNode?.insertBefore(wrapper, element)
  wrapper.appendChild(element)
  wrapper.appendChild(touchArea)
  
  // Forward events from touch area to original element
  touchArea.addEventListener('click', (e) => {
    e.stopPropagation()
    element.click()
  })
  
  touchArea.addEventListener('touchstart', (e) => {
    e.stopPropagation()
    // Forward touch event to original element
    const rect = element.getBoundingClientRect()
    const touchAreaRect = touchArea.getBoundingClientRect()
    const offsetX = rect.left - touchAreaRect.left
    const offsetY = rect.top - touchAreaRect.top
    
    // Create a new touch event with adjusted coordinates
    const touch = e.touches[0]
    const adjustedTouch = new Touch({
      identifier: touch.identifier,
      target: element,
      clientX: touch.clientX + offsetX,
      clientY: touch.clientY + offsetY,
      pageX: touch.pageX + offsetX,
      pageY: touch.pageY + offsetY,
      screenX: touch.screenX,
      screenY: touch.screenY,
      radiusX: touch.radiusX || 0,
      radiusY: touch.radiusY || 0,
      rotationAngle: touch.rotationAngle || 0,
      force: touch.force || 1
    })
    
    const newEvent = new TouchEvent('touchstart', {
      bubbles: e.bubbles,
      cancelable: e.cancelable,
      touches: [adjustedTouch],
      targetTouches: [adjustedTouch],
      changedTouches: [adjustedTouch]
    })
    element.dispatchEvent(newEvent)
  })
  
  return () => {
    // Cleanup: restore original structure
    wrapper.parentNode?.insertBefore(element, wrapper)
    wrapper.remove()
  }
}